package com.example.demo;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.transaction.Transactional;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.orm.hibernate5.HibernateCallback;
import org.springframework.orm.hibernate5.support.HibernateDaoSupport;

@Transactional
public class GenericDaoImpl<T, PK extends Serializable> extends HibernateDaoSupport implements GenericDao<T, PK> {

	@Autowired
	public void InitSession(SessionFactory sessionFactory) {
		setSessionFactory(sessionFactory);
	}

	private Class<T> clazz;

	@SuppressWarnings("unchecked")
	public GenericDaoImpl() {
		clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
	}

	/**
	 * 根据指定的Hql语句进行数据库查询
	 * 
	 * @param hql
	 *            hql语句
	 * @return 对象集合
	 */
	@SuppressWarnings("unchecked")
	protected List<T> queryByHql(String hql) {
		return (List<T>) getHibernateTemplate().find(hql);
	}

	@SuppressWarnings("unchecked")
	protected List<T> queryByHql(String hql, Object... params) {
		return (List<T>) getHibernateTemplate().find(hql, params);
	}

	/**
	 * 根据指定的Hql语句进行数据库查询
	 * 
	 * @param hql
	 *            hql语句
	 * @param params
	 *            查询参数
	 * @return 对象集合
	 */
	@SuppressWarnings("unchecked")
	protected List<T> queryByHql(String hql, Map<String, ?> params) {
		NamedParameterPair namedParameterPair = new NamedParameterPair(params);
		return (List<T>) getHibernateTemplate().findByNamedParam(hql, namedParameterPair.getParamNames(),
				namedParameterPair.getValues());
	}

	protected T findFirst(String hql, Map<String, ?> params) {
		List<T> list = queryByHql(hql, params);
		if (list.isEmpty()) {
			return null;
		}
		return list.get(0);
	}

	protected T findFirst(String hql, Object... params) {
		List<T> list = queryByHql(hql, params);
		if (list.isEmpty()) {
			return null;
		}
		return list.get(0);
	}

	@SuppressWarnings("unchecked")
	protected T findSingle(String hql, Object... params) {
		List<T> list = (List<T>) getHibernateTemplate().find(hql, params);
		return DataAccessUtils.singleResult(list);
	}

	protected T findSingle(String hql, Map<String, ?> params) {
		List<T> list = queryByHql(hql, params);
		return DataAccessUtils.singleResult(list);
	}

	public void save(Iterable<T> iterable, int size) {
		int index = 1;
		for (T item : iterable) {
			getHibernateTemplate().save(item);
			if (index % size == 0) {
				getHibernateTemplate().flush();
				getHibernateTemplate().clear();
			}
			index++;
		}
	}

	@Override
	public void save(T entity) {
		getHibernateTemplate().save(entity);
	}

	@Override
	public T findById(PK id) {
		return getHibernateTemplate().get(clazz, id);
	}

	@Override
	public void update(T entity) {
		getHibernateTemplate().update(entity);
	}

	public void update(Iterable<T> iterable, int size) {
		int index = 1;
		for (T item : iterable) {
			getHibernateTemplate().update(item);
			if (index % size == 0) {
				getHibernateTemplate().flush();
				getHibernateTemplate().clear();
			}
			index++;
		}
	}

	@Override
	public void saveOrUpdate(T entity) {
		getHibernateTemplate().saveOrUpdate(entity);
	}

	@Override
	public void delete(T entity) {
		getHibernateTemplate().delete(entity);
	}

	@Override
	public void deleteAll(List<T> entities) {
		getHibernateTemplate().deleteAll(entities);
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> findAll(Object... args) {
		return (List<T>) getHibernateTemplate().find("from " + clazz.getName(), args);
	}

	@Override
	public List<T> queryPage(T entity, int page, int rows) {
		return getHibernateTemplate().findByExample(entity, (page - 1) * rows, rows);
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public Object getUniqueObject(final String hql, final Object... params) {
		Object result = null;
		result = getHibernateTemplate().executeWithNativeSession(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException {
				Query query = session.createQuery(hql);
				if (params != null) {
					for (int i = 0; i < params.length; i++) {
						query.setParameter(i, params[i]);
					}
				}
				return query.uniqueResult();
			}
		});
		return result;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public void deletebyhql(final String hql) {
		getHibernateTemplate().executeWithNativeSession(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException {
				session.createQuery(hql).executeUpdate();
				return 1;
			}
		});
	}

	private class NamedParameterPair {

		private final String[] paramNames;

		private final Object[] values;

		public NamedParameterPair(Map<String, ?> parameters) {
			if (parameters == null) {
				parameters = new HashMap<String, Object>();
			}
			paramNames = new String[parameters.size()];
			values = new Object[parameters.size()];
			int i = 0;
			for (Map.Entry<String, ?> param : parameters.entrySet()) {
				paramNames[i] = param.getKey();
				values[i] = param.getValue();
				i++;
			}
		}

		public String[] getParamNames() {
			return paramNames;
		}

		public Object[] getValues() {
			return values;
		}
	}

}